package io.gitee.hechenl.easyopc.opcua;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedSubscription;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@AllArgsConstructor
@Slf4j
public class EasyOpcUaClient {

    private String appName;
    private String appUri;
    private boolean enableSubscript;
    private EasyNodeIdContext easyNodeIdContext;
    private final EasyOpcUaConsumer consumer;


    private final Map<String, OpcUaClient> activeClientPool = new ConcurrentHashMap<>();
    private final Map<String, ManagedSubscription> subscriptPool = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() throws Exception {
        accessInit();
        if (enableSubscript) {
            subscriptInit();
        }
    }

    private void accessInit() throws Exception {
        List<EasyNodeId> accessNodeIdList = easyNodeIdContext.getAccessNodeIdList();
        if (CollectionUtils.isEmpty(accessNodeIdList)) {
            throw new RuntimeException("EasyOpcUaClient accessInit error, accessNodeIdList is empty");
        }
        for (EasyNodeId easyNodeId : accessNodeIdList) {
            OpcUaClient client = EasyOpcUaUtil.createClient(easyNodeId.getServerIp(), appName, appUri);
            client.connect().get();
            activeClientPool.put(easyNodeId.getServerIp(), client);
        }
    }

    private void subscriptInit() throws Exception {
        List<EasyNodeId> subNodeIdList = easyNodeIdContext.getSubNodeIdList();
        if (!CollectionUtils.isEmpty(subNodeIdList)) {
            for (EasyNodeId easyNodeId : subNodeIdList) {
                OpcUaClient opcUaClient = activeClientPool.get(easyNodeId.getServerIp());
                if (opcUaClient == null) {
                    throw new RuntimeException("EasyOpcUaClient subscriptInit error, activeClientPool is empty");
                }
                ManagedSubscription subscript = EasyOpcUaUtil.subscript(opcUaClient, easyNodeId.getNodeIds(), consumer);
                subscriptPool.put(easyNodeId.getServerIp(), subscript);
            }
        }
    }

    @PreDestroy
    public void shutdown() {
        if (!CollectionUtils.isEmpty(activeClientPool)) {
            activeClientPool.forEach((serverUrl, client) -> {
                try {
                    if (subscriptPool.get(serverUrl) != null) {
                        subscriptPool.get(serverUrl).delete();
                    }
                    client.disconnect().get();
                } catch (Exception e) {
                    log.error("Opc Ua shutdown client disconnect error, serverUrl:{}", serverUrl, e);
                }
            });
        }
    }

    public Object read(Integer namespace, String identifier) {
        String serverIP = easyNodeIdContext.getServerIP(namespace, identifier);
        if (!StringUtils.hasText(serverIP)) {
            throw new RuntimeException("EasyOpcUaClient read error, not found available serverIP for namespace:"
                    + namespace + ", id:" + identifier);
        }
        OpcUaClient client = activeClientPool.get(serverIP);
        if (client == null) {
            throw new RuntimeException("EasyOpcUaClient read error, activeClient is empty, serverIP:" + serverIP);
        }
        return EasyOpcUaUtil.read(client, new NodeId(namespace, identifier));
    }

}
